﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

namespace VerificadorF3101
{

    public delegate void ProgressChangedEventHandler(object sender, ProgressEventArgs e);
    public delegate void StatusReportedEventHandler(object sender, StatusEventArgs e);


    public class Verificador
    {
        public Verificador(string carpeta)
        {
            this.Carpeta = carpeta;
        }

        public string Carpeta { get; set; }

        public event ProgressChangedEventHandler ProgressChanged;

        public event StatusReportedEventHandler StatusReported;

        public void Verificar()
        {
            try
            {
                Dictionary<string, F3101> directorios = new Dictionary<string, F3101>();

                OnStatusReported("Analizando la carpeta: '" + Carpeta + "'.");

                // Selecciono todos los directorios llamados "COMPROBANTES"
                var dirComprobantes = Directory.GetDirectories(Carpeta, "COMPROBANTES", SearchOption.AllDirectories);

                OnStatusReported(String.Format("Se encontraron {0} direcorios COMPROBANTES.", dirComprobantes.Length));

                // Obtengo todos los archivos de comprobantes y de formularios y los pongo en un diccionario
                foreach (var dir in dirComprobantes)
                {
                    OnStatusReported(String.Format("Buscando comprobantes en '{0}'", dir));

                    var nombreComprobantes = Directory.GetFiles(dir, "afip_presentacion_cuit_*_f3101_nrotransaccion_*.pdf", SearchOption.TopDirectoryOnly);

                    OnStatusReported(String.Format("Se encontraron {0} comprobantes.", nombreComprobantes.Length));

                    var dirF3101 = dir.Replace("COMPROBANTES", "F3101");

                    OnStatusReported(String.Format("Buscando Formularios en '{0}'", dirF3101));

                    var nombreFormularios = Directory.GetFiles(dirF3101, "F3101.*.pdf", SearchOption.AllDirectories);

                    OnStatusReported(String.Format("Se encontraron {0} formularios.", nombreFormularios.Length));

                    Comprobante[] comprobantes = nombreComprobantes.Select(c => new Comprobante { NombreArchivo = c }).ToArray();
                    Formulario[] formularios = nombreFormularios.Select(c => new Formulario { NombreArchivo = c }).ToArray();

                    directorios.Add(dir, new F3101 { Comprobantes = comprobantes, Formularios = formularios });
                }

                int total = directorios.SelectMany(d => d.Value.Comprobantes).Count();
                int progress = 0;

                // Recorro todos los directorios y verifico la integridad de los documentos
                foreach (var dir in directorios)
                {
                    OnStatusReported(String.Format("Analizando comprobantes en '{0}'", dir));

                    // Verifico que existan los formularios q indican los comprobantes
                    foreach (var comprobante in dir.Value.Comprobantes)
                    {
                        progress++;

                        OnStatusReported(String.Format("Leyendo comprobante '{0}'.", comprobante.NombreArchivo));

                        OnProgressChanged(Convert.ToInt32((progress / total) * 100));

                        PdfDocument doc = PdfReader.Open(comprobante.NombreArchivo, PdfDocumentOpenMode.ReadOnly);

                        comprobante.NombreFormulario = ExtraerNombreFormulario(doc);

                        OnStatusReported(String.Format("Comprobante correspondiente al formulario '{0}'", comprobante.NombreFormulario));

                        Formulario formulario = dir.Value
                            .Formularios
                            .Where(f => f.NombreArchivo.EndsWith(comprobante.NombreFormulario))
                            .FirstOrDefault();

                        if (formulario != null)
                        {
                            OnStatusReported(String.Format("Se encontró el formulario '{0}'", formulario.NombreArchivo));

                            comprobante.FormularioEncontrado = true;
                            comprobante.Formulario = formulario;

                            formulario.ComprobanteEncontrado = true;
                            formulario.Comprobante = comprobante;
                        }
                        else
                        {
                            OnStatusReported(String.Format("No se encontró el formulario '{0}'", comprobante.NombreFormulario));
                        }

                        AnalizarFormularios(dir.Value.Formularios);
                    }



                    GenerarSalida(dir);
                }

            }
            catch (Exception ex)
            {
                OnStatusReported(ex.Message);
            }

            return;
        }

        private void AnalizarFormularios(Formulario[] formularios)
        {
            foreach (var f in formularios)
            {
                PdfDocument doc = PdfReader.Open(f.NombreArchivo, PdfDocumentOpenMode.ReadOnly);
                
                string text = ExtractText(doc);

                string[] nombre = f.NombreArchivo.Split('.');
                string legajo = nombre[1];
                string familia = nombre[2].Replace("F", "");

                string[] adjuntos = text.Split(' ').Where(t => t.EndsWith(".PDF")).ToArray();
                foreach (var adjunto in adjuntos)
                {
                    string[] partes = adjunto.Split('_');

                    // Verifico el legajo
                    if (!partes[0].Equals(legajo))
                        f.AdjuntosOK = false;
                    else if (!partes[1].Equals(familia))
                        f.AdjuntosOK = false;
                    else
                        f.AdjuntosOK = true;
                }
            }
        }

        private void GenerarSalida(KeyValuePair<string, F3101> directorio)
        {
            Comprobante[] comprobantesSinFormularios = directorio.Value.Comprobantes.Where(c => c.FormularioEncontrado == false).ToArray();
            Comprobante[] comprobantesConFormularios = directorio.Value.Comprobantes.Except(comprobantesSinFormularios).ToArray();
            Formulario[] formulariosSinComprobantes = directorio.Value.Formularios.Where(c => c.ComprobanteEncontrado == false).ToArray();
            Formulario[] formulariosConAdjuntosIncorrectos = directorio.Value.Formularios.Where(c => c.AdjuntosOK == false).ToArray();

            bool conErrores = (comprobantesSinFormularios.Length > 0 || formulariosSinComprobantes.Length > 0 || formulariosConAdjuntosIncorrectos.Length > 0);

            string path = directorio.Key;
            string fileName = String.Format("{0}resultados_{1}.txt", path.Replace("COMPROBANTES", ""), conErrores ? "error" : "ok");

            using (StreamWriter archivoSalida = File.CreateText(fileName))
            {
                archivoSalida.AutoFlush = true;

                archivoSalida.WriteLine(String.Format("Resultados de la comprobación ejecutada el {0}:", DateTime.Now));
                archivoSalida.WriteLine(String.Empty);
                archivoSalida.WriteLine("ERRORES > COMPROBANTES SIN FORMULARIO:");
                archivoSalida.WriteLine(String.Empty);

                foreach (var c in comprobantesSinFormularios)
                {
                    archivoSalida.WriteLine(String.Format("ERROR > Comprobante: {0} \t Formulario: {1}", c.NombreArchivo, c.NombreFormulario));
                }

                archivoSalida.WriteLine(String.Empty);
                archivoSalida.WriteLine("ERRORES > FORMULARIOS SIN COMPROBANTE:");
                archivoSalida.WriteLine(String.Empty);

                foreach (var f in formulariosSinComprobantes)
                {
                    archivoSalida.WriteLine(String.Format("ERROR > Formulario: {0}", f.NombreArchivo));
                }

                archivoSalida.WriteLine(String.Empty);
                archivoSalida.WriteLine("ERRORES > FORMULARIOS CON ADJUNTOS INCORRECTOS:");
                archivoSalida.WriteLine(String.Empty);

                foreach (var f in formulariosConAdjuntosIncorrectos)
                {
                    archivoSalida.WriteLine(String.Format("ERROR > Formulario: {0}", f.NombreArchivo));
                }

                archivoSalida.WriteLine(String.Empty);
                archivoSalida.WriteLine("FORMULARIOS CON COMPROBANTE:");
                archivoSalida.WriteLine(String.Empty);

                foreach (var c in comprobantesConFormularios)
                {
                    archivoSalida.WriteLine(String.Format("OK > Comprobante: {0} \t Formulario: {1}", c.NombreArchivo, c.NombreFormulario));
                }

                archivoSalida.Flush();
                archivoSalida.Close();

                OnStatusReported(String.Format("Se generó correctamente el archivo '{0}'", fileName));

            }
        }

        private string ExtraerNombreFormulario(PdfDocument doc)
        {
            string text = ExtractText(doc);

            int startIndex = text.IndexOf("[F3101.");
            int endIndex = text.IndexOf(".pdf]");

            string nombreFormulario = text.Substring(startIndex + 1, endIndex - startIndex + 3);

            return nombreFormulario;
        }

        public string ExtractText(PdfDocument inputDocument)
        {
            string outputText = "";

            foreach (PdfPage page in inputDocument.Pages)
            {
                for (int index = 0; index < page.Contents.Elements.Count; index++)
                {

                    PdfDictionary.PdfStream stream = page.Contents.Elements.GetDictionary(index).Stream;
                    outputText += new PdfParser().ExtractTextFromPDFBytes(stream.Value);
                }
            }
            
            return outputText;
        }

        protected virtual void OnProgressChanged(int newProgress)
        {
            if (ProgressChanged != null)
                ProgressChanged(this, new ProgressEventArgs { Progress = newProgress });
        }

        protected virtual void OnStatusReported(string status)
        {
            if (StatusReported != null)
                StatusReported(this, new StatusEventArgs { Status = status });
        }
    }

    public class F3101
    {
        public Comprobante[] Comprobantes { get; set; }

        public Formulario[] Formularios { get; set; }
    }

    public class Comprobante
    {
        public string NombreArchivo { get; set; }
        public string NombreFormulario { get; set; }
        public bool FormularioEncontrado { get; set; }
        public Formulario Formulario { get; set; }
    }

    public class Formulario
    {
        public string NombreArchivo { get; set; }
        public bool ComprobanteEncontrado { get; set; }
        public Comprobante Comprobante { get; set; }
        public bool AdjuntosOK { get; set; }
    }

    public class ProgressEventArgs : EventArgs
    {
        public int Progress { get; set; }
    }
    public class StatusEventArgs : EventArgs
    {
        public string Status { get; set; }
    }
}
